/*
 * Copyright (C) 2008 Universidade Federal de Campina Grande
 *  
 * This file is part of OurGrid. 
 *
 * OurGrid is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free 
 * Software Foundation, either version 3 of the License, or (at your option) 
 * any later version. 
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public License 
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package org.ourgrid.peer.business.dao;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.ourgrid.common.interfaces.to.LocalWorkerState;
import org.ourgrid.common.internal.IResponseTO;
import org.ourgrid.common.spec.worker.WorkerSpec;
import org.ourgrid.common.spec.worker.WorkerSpecConstants;
import org.ourgrid.common.statistics.beans.peer.Attribute;
import org.ourgrid.common.statistics.beans.peer.Peer;
import org.ourgrid.common.statistics.beans.peer.Worker;
import org.ourgrid.common.statistics.beans.status.WorkerStatus;
import org.ourgrid.peer.to.LocalWorker;
import org.ourgrid.reqtrace.Req;
import org.ourgrid.worker.WorkerConstants;

import br.edu.ufcg.lsd.commune.identification.ContainerID;
import br.edu.ufcg.lsd.commune.identification.ServiceID;

/**
 * Stores <code>LocalWorker</code>s, that is, workers that were set by the
 * 'setpeers' command
 */
public class LocalWorkersDAO {

	private static final long serialVersionUID = 1L;

	private final Map< String, String > workersPK = new HashMap< String, String >();

	/**
	 * Get the LocalWorker of a Worker
	 * 
	 * @param entityID The entityID of the Worker
	 * @return the LocalWorker
	 */
	@Req({"REQ025","REQ019"})
	public LocalWorker getLocalWorker(List<IResponseTO> responses, String workerUserAtServer) {
		
		Worker worker = PeerDAOFactory.getInstance().getWorkerDAO().findActiveWorker(responses, workerUserAtServer);
		
		if (worker == null) {
			return null;
		}
		
		Map< String, String > attributes = new LinkedHashMap< String, String >();
		Map< String, String > annotations = new LinkedHashMap< String, String >();

		for ( Attribute att : worker.getAttributes() ) {
			if ( att.getIsAnnotation() ) {
				annotations.put( att.getProperty(), att.getValue() );
			} else {
				attributes.put( att.getProperty(), att.getValue() );
			}
		}
		
		WorkerSpec spec;
		
		String expressionValue = attributes.get( WorkerSpecConstants.EXPRESSION );
		if(expressionValue != null && !expressionValue.isEmpty()){
			spec = new WorkerSpec( expressionValue );
		}else{
			spec = new WorkerSpec( attributes, annotations );
		}

		String workerPK = workersPK.get( workerUserAtServer );
		
		LocalWorker localWorker = new LocalWorker(spec, workerUserAtServer, workerPK);
		WorkerStatus currentStatus = worker.getStatus();

		if ( currentStatus != null ) {
			LocalWorkerState status = getLocalWorkerStateFor( currentStatus );
			localWorker.setStatus( status );
		}
		
		return localWorker;
	}

	/**
	 * Get the LocalWorkers of this peer on the specified status
	 * @param status The status to filter the LocalWorkers
	 * @return All of the LocalWorkers in the specified status
	 */
	@Req("REQ036")
	public Collection<LocalWorker> getLocalWorkers(List<IResponseTO> responses,
			String peerUserAtServer, LocalWorkerState status) {
		Collection<LocalWorker> result = new LinkedList<LocalWorker>();
		
		Peer workerPeer = PeerDAOFactory.getInstance().getPeerDAO().findByID(responses, peerUserAtServer);
		
		Collection<Worker> allActiveWorkers = PeerDAOFactory.getInstance().getWorkerDAO().findAllActiveWorkers(
				responses, peerUserAtServer);
		for (Worker worker : allActiveWorkers) {
			WorkerStatus currentStatus = worker.getStatus();
			LocalWorkerState localState = getLocalWorkerStateFor(currentStatus);
			if (localState.equals(status) && workerPeer.equals(worker.getPeer())) {
				result.add(getLocalWorker(responses, worker.getAddress()));
			}
			
		}
		
		return result;
	}
	
	private LocalWorkerState getLocalWorkerStateFor(
			WorkerStatus currentStatus) {
		
		LocalWorkerState status = null;
		
		if (WorkerStatus.CONTACTING.equals(currentStatus)) {
			status = LocalWorkerState.CONTACTING;
		} else if (WorkerStatus.DONATED.equals(currentStatus)) {
			status = LocalWorkerState.DONATED;
		} else if (WorkerStatus.IDLE.equals(currentStatus)) {
			status = LocalWorkerState.IDLE;
		} else if (WorkerStatus.IN_USE.equals(currentStatus)) {
			status = LocalWorkerState.IN_USE;
		} else if (WorkerStatus.OWNER.equals(currentStatus)) {
			status = LocalWorkerState.OWNER;
		} else if (WorkerStatus.REMOVED.equals(currentStatus)) {
			status = LocalWorkerState.REMOVED;
		} else if (WorkerStatus.ERROR.equals(currentStatus)) {
			status = LocalWorkerState.ERROR;
		} else if (WorkerStatus.UNAVAILABLE.equals(currentStatus)) {
			status = LocalWorkerState.UNAVAILABLE;
		}
		
		return status;
	}

	/**
	 * Verifies if certain Worker is not already on this manager
	 * @param workerEntityID The worker entityID to be verified
	 * @return true if it's a new worker, false, otherwise
	 */
	@Req("REQ010")
	public boolean isNewWorker(List<IResponseTO> responses, String workerUserAtServer) {
		return getLocalWorker(responses, workerUserAtServer) == null;
	}
	

	/**
	 * Get the EntityIDs of all the workers stored on this manager
	 * @return The entityIDs of the store workers
	 */
	@Req("REQ010")
	public Collection<String> getLocalWorkersUserAtServer(List<IResponseTO> responses, String myUserAtServer){
		Collection<String> result = new LinkedList<String>();
		
		Peer workerPeer = PeerDAOFactory.getInstance().getPeerDAO().findByID(responses, myUserAtServer);
		
		Collection<Worker> allActiveWorkers = PeerDAOFactory.getInstance().getWorkerDAO().findAllActiveWorkers(responses, myUserAtServer);
		for (Worker worker : allActiveWorkers) {
			if (workerPeer.equals(worker.getPeer())) {
				//result.add(parseServiceID(worker.getAddress()));
				result.add(worker.getAddress());
			}	
		}
		
		return result;
	}

	public static String getUserAtServer(ServiceID serviceID) {
		return serviceID.getUserName() + '@' + serviceID.getServerName();
	}
	
	public static ServiceID parseServiceID(String address) {
		String[] splitAddress = address.split("@");
		String userName = splitAddress[0];
		String serverName = splitAddress[1];
		
		return new ServiceID(
				new ContainerID(userName, serverName, WorkerConstants.MODULE_NAME), 
				WorkerConstants.LOCAL_WORKER_MANAGEMENT);
	}
	
	/**
	 * Get the LocalWorker of a Worker
	 * @param workerPublicKey The public key of the Worker
	 * @return the LocalWorker or null it does not exists for this public key
	 */
	@Req("REQ027")
	public LocalWorker getRecoveredWorker(List<IResponseTO> responses, String workerUserAtServer) {
		LocalWorker localWorker = getLocalWorker(responses, workerUserAtServer);
		if (localWorker == null || !localWorker.isUp()) {
			return null;
		}
		
		return localWorker;
	}

	public void workerIsUp(LocalWorker localWorker) {
		String userAtServer = localWorker.getWorkerUserAtServer();
		this.workersPK.put(userAtServer, localWorker.getPublicKey());
	}

	public void workerIsDown(LocalWorker localWorker) {
	}

}